package engine;

import engine.Tweet.Brand;
import engine.Tweet.Polarity;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.swing.SwingWorker;
import normalisation.Normalizer;
import ui.UserInterface;
import utilities.FileTools;

public class Stats extends SwingWorker<Void, Integer> {

    private String trainPath;
    private UserInterface gui;//pour update la progression
    private ArrayList<String> vocabulary;//index id -> mot
    private TreeMap<String, Integer> wordID;//index mot -> id
    private Tweet[] tweets;
    private Normalizer normalizer;
    private TreeMap<String, Integer> corpusTfMap;
    //Stats
    private Integer uniqueWordsCount;
    private Integer totalWordsCount;
    private Integer tweetsCount;
    private Integer avgWordCount;

    public Stats(String trainPath, Normalizer normalizer) {
        this.trainPath = trainPath;
        this.normalizer = normalizer;
    }

    public Tweet[] getTweets() {
        return tweets;
    }

    public ArrayList<String> getVocabulary() {
        return vocabulary;
    }

    public Integer getUniqueWordsCount() {
        return uniqueWordsCount;
    }

    public Integer getTotalWordsCount() {
        return totalWordsCount;
    }

    public Integer getTweetsCount() {
        return tweetsCount;
    }

    public Integer getAvgWordCount() {
        return avgWordCount;
    }

    public UserInterface getGui() {
        return gui;
    }

    public void setGui(UserInterface gui) {
        this.gui = gui;
    }

    public TreeMap<String, Integer> getCorpusTfMap() {
        return corpusTfMap;
    }

    private Polarity parsePolarity(String polarityName) {
        for (Polarity p : Polarity.values()) {
            if (polarityName.equals(p.name())) {
                return p;
            }
        }

        return Polarity.unknown;
    }

    private Brand parseBrand(String brandName) {
        for (Brand b : Brand.values()) {
            if (brandName.equals(b.name())) {
                return b;
            }
        }

        return Brand.unknown;
    }

    public void makeStats() throws IOException {
        //Stats
        uniqueWordsCount = 0;
        totalWordsCount = 0;
        tweetsCount = 0;
        avgWordCount = 0;

        Tweet currentTweet = new Tweet();//Tweet courant
        List<String> lines;//lignes du fichier courant
        ArrayList<String> words;//mots du fichier courant
        Integer currentLine = 0;//numero de la ligne courante
        Integer progress;//Progression de la fonction en %
        TreeMap<String, Integer> tfMap;//TF Map du tweet courant
        TreeMap<String, Integer>[] tfMaps;//Tableau des tfMap de chaque tweet
        TreeMap<String, Integer> dfMap;//DF Map du fichier
        Integer currentWordTF;//TF du mot courant
        Integer currentWordDF;//DF du mot courant
        Integer corpusCurrentWordTf;//TF courant pour la map corpusTfMap

        //Parcours de chaque ligne
        lines = FileTools.readLinesTextFile(trainPath, normalizer.getCharset());

        //Init
        tweets = new Tweet[lines.size()];
        tfMaps = new TreeMap[lines.size()];
        dfMap = new TreeMap<String, Integer>();
        corpusTfMap = new TreeMap<String, Integer>();


        for (String line : lines) {
            tweetsCount++;

            //suppression première parenthèse (doit être le premier caractère)
            line = line.substring(1);

            //parse la polarité
            currentTweet.setPolarity(parsePolarity(line.substring(0, line.indexOf(","))));
            line = line.substring(line.indexOf(",") + 1);//on supprime "polarité,"

            //parse la marque
            currentTweet.setBrand(parseBrand(line.substring(0, line.indexOf(")"))));
            line = line.substring(line.indexOf(")") + 2);//on supprime "marque) "

            //Init tfMap de la ligne courante
            tfMap = new TreeMap<String, Integer>();

            //Parcours de chaque mot
            words = normalizer.normalize(line);
            for (String word : words) {

                //Update tfMap & dfMap
                if (tfMap.containsKey(word)) {//Mot deja rencontré sur cette ligne
                    currentWordTF = tfMap.get(word);
                } else {//Mot vu pour la première fois sur la ligne
                    currentWordTF = 0;

                    //Update DF Map
                    currentWordDF = (dfMap.containsKey(word)) ? dfMap.get(word) : 0;
                    dfMap.put(word, currentWordDF + 1);
                }
                tfMap.put(word, currentWordTF + 1);

                //Update corpusTfMap
                corpusCurrentWordTf = (corpusTfMap.containsKey(word)) ? corpusTfMap.get(word) : 0;
                corpusTfMap.put(word, corpusCurrentWordTf + 1);
            }

            //Stock la tfMap
            tfMaps[currentLine] = tfMap;

            tweets[currentLine] = new Tweet(currentTweet);

            currentLine++;
            progress = 100 * currentLine / lines.size();
            publish(progress);//Appel process qui update GUI
            //System.out.println(progress + "%");
        }

        //Toutes les lignes ont été parcourues
        publish(99);//Set la progression a 99%

        //Vocabulaire
        vocabulary = new ArrayList<String>();
        wordID = new TreeMap<String, Integer>();
        Integer uniqueWordIndex = 0;

        for (Map.Entry<String, Integer> e : dfMap.entrySet()) {
            if (normalizer.getFilterOneOccurence() && e.getValue() > 1
                    || !normalizer.getFilterOneOccurence()) {//Filter mots avec une seule occurence
                vocabulary.add(e.getKey());
                wordID.put(e.getKey(), uniqueWordIndex);
                uniqueWordIndex++;
                totalWordsCount += e.getValue();
            }
        }

        //Création du vecteur TF-IDF pour chaque Tweet
        System.out.println("Creation de " + lines.size() + " vecteurs ...");
        //VectorTILowMem currentVector;
        VectorTI currentVector;
        double currentTfIDF;
        for (int i = 0; i < lines.size(); i++) {

            currentVector = new VectorTI();
            //pour chaque mot du vocabulaire
            for (String word : vocabulary) {
                //si le mot n'est pas dans la tf du tweet, alors tfIdf = 0.
                if (!tfMaps[i].containsKey(word)) {
                    currentTfIDF = 0d;
                } else {
                    currentTfIDF = (double) tfMaps[i].get(word) * Math.log10((double) lines.size() / dfMap.get(word));
                }

                currentVector.add(currentTfIDF);
            }
            
            currentVector.normalize();
            tweets[i].setVector(currentVector);

//            currentVector = new VectorTILowMem();
//            //Pour chaque mot de la tfMap du tweet
//            for (String word : tfMaps[i].keySet()) {
//                if (!wordID.containsKey(word)) {//Le mot a été filtré car une seule eoccurence
//                    continue;//On passe au mot suivant
//                }
//                
//                //Calcul TF.IDF
//                currentTfIDF = (double) tfMaps[i].get(word) * Math.log10((double) lines.size() / dfMap.get(word));
//                
//                //wordID.get(word) donne l'index du mot word dans le vecteur
//                currentVector.add(wordID.get(word), currentTfIDF);
//            }
//            
//            //Normalisation et ajout
//            currentVector.normalize();

//            tweets[i].setVector(currentVector);

            //System.out.println(i + "   " + currentVector.toString(vocabulary));
            //System.out.println(i + "   " + currentVector.toString());
        }

        //Nb Mots uniques
        uniqueWordsCount = vocabulary.size();

        //Nb mots en moyenne par ligne
        avgWordCount = totalWordsCount / lines.size();

        publish(100);//Set la progression a 100%
        
        System.out.println("...done.");
        //printDF(dfMap);
    }

    //Affiche la map document frequency (pour tester)
    public void printDF(Map<String, Integer> dfMap) {
        String print = "";
        Integer entryPerLine = 50;
        Integer counter = 0;

        for (Map.Entry<String, Integer> e : dfMap.entrySet()) {
            //if (e.getValue() == 1) {//Une seule occurence
            print += e.getKey() + ": " + e.getValue() + ",  ";
            counter++;

            if (counter % entryPerLine == 0) {
                print += "\n";
            }
            //}
        }

        System.out.println(print);
    }

    @Override
    protected Void doInBackground() throws Exception {
        makeStats();

        return null;
    }

    @Override
    protected void process(List<Integer> values) {
        for (Integer val : values) {
            gui.updateBarStats(val);
        }
    }

    @Override
    protected void done() {
        gui.statsFilled();
    }
}
